package cn.bugstack.mybatis.scripting.xmltags;

import cn.bugstack.mybatis.session.Configuration;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.StringTokenizer;

/**
 * process trim tag
 * @author xingshujing
 */
public class TrimSqlNode implements SqlNode{


    private Configuration configuration;

    private MixedSqlNode mixedSqlNode;

    private String prefix;

    private List<String> prefixOverrides;

    private String suffix;

    private List<String> suffixOverrides;

    public TrimSqlNode(Configuration configuration, MixedSqlNode mixedSqlNode, String prefix, String prefixOverrides, String suffix, String suffixOverrides) {
        this.configuration = configuration;
        this.mixedSqlNode = mixedSqlNode;
        this.prefix = prefix;
        this.prefixOverrides = parseOverrides(prefixOverrides);
        this.suffix = suffix;
        this.suffixOverrides = parseOverrides(suffixOverrides);
    }

    private List<String> parseOverrides(String overrides) {
        if (overrides != null) {
            StringTokenizer stringTokenizer = new StringTokenizer(overrides, "|", false);
            List<String> list = new ArrayList<>(stringTokenizer.countTokens());
            while (stringTokenizer.hasMoreTokens()) {
                list.add(stringTokenizer.nextToken().toUpperCase(Locale.ENGLISH));
            }
            return list;
        }
        return Collections.emptyList();
    }

    public static void main(String[] args) {
        StringTokenizer stringTokenizer = new StringTokenizer("a|b", "|", false);
        System.out.println(stringTokenizer.countTokens());
    }

    @Override
    public boolean apply(DynamicContext context) {
        FilteredDynamicContext filteredDynamicContext = new FilteredDynamicContext(context);
        boolean result = mixedSqlNode.apply(filteredDynamicContext);
        filteredDynamicContext.applyAll();
        return result;
    }

    /**
     * 实现trim功能，trim prefix and suffix
     * 处理 trim tag 内部的sql section
     */
    private class FilteredDynamicContext extends DynamicContext {

        private DynamicContext delegate;

        private boolean prefixApplied;

        private boolean suffixApplied;

        private StringBuilder sqlBuffer;



        public FilteredDynamicContext(DynamicContext delegate) {
            super(configuration, null);
            this.delegate = delegate;
            this.prefixApplied = false;
            this.suffixApplied = false;
            this.sqlBuffer = new StringBuilder();
        }

        public String getSql() {
            return delegate.getSql();
        }
        public void appendSql(String sql) {
            sqlBuffer.append(sql);
        }

        public Map<String, Object> getBindings() {
            return delegate.getBindings();
        }

        /**
         * process prefix and suffix
         * then append trimmed sql to previous sql
         */
        public void applyAll() {
            sqlBuffer = new StringBuilder(sqlBuffer.toString().trim());
            // 转换成大写，避免大小写问题
            String trimmedUppercaseSql = sqlBuffer.toString().toUpperCase(Locale.ENGLISH);
            // 去除多余的前后缀，最后加上前后缀
            if (trimmedUppercaseSql.length() > 0) {
                applyPrefix(sqlBuffer, trimmedUppercaseSql);
                applySuffix(sqlBuffer, trimmedUppercaseSql);
            }
            // 拼接sql
            delegate.appendSql(sqlBuffer.toString());
        }

        private void applySuffix(StringBuilder sql, String trimmedUppercaseSql) {
            if (!suffixApplied) {
                suffixApplied = true;
                // delete redundant prefix
                if (prefixOverrides != null) {
                    for (String suffixOverride : suffixOverrides) {
                        if (trimmedUppercaseSql.endsWith(suffixOverride)) {
                            int start = sql.length() - suffixOverride.trim().length();
                            int end = sql.length();
                            sql.delete(start,end);
                            break;
                        }
                    }
                }
                // insert prefix
                if (suffix != null) {
                    sql.append(" ");
                    sql.append(suffix);
                }
            }
        }

        private void applyPrefix(StringBuilder sql, String trimmedUppercaseSql) {
            if (!prefixApplied) {
                prefixApplied = true;
                // delete redundant prefix
                if (prefixOverrides != null) {
                    for (String prefixOverride : prefixOverrides) {
                        if (trimmedUppercaseSql.startsWith(prefixOverride)) {
                            sql.delete(0, prefixOverride.length());
                            break;
                        }
                    }
                }
                // insert prefix
                if (prefix != null) {
                    sql.insert(0, " ");
                    sql.insert(0, prefix);
                }
            }
        }
    }
}
